package com.huyanqi.core.dao.impl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityNotFoundException;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.impl.CriteriaImpl.OrderEntry;
import org.hibernate.transform.ResultTransformer;
import org.skyway.spring.util.dao.AbstractJpaDao;
import org.springframework.transaction.annotation.Transactional;

import com.huyanqi.core.Page;
import com.huyanqi.core.ReflectionUtils;
import com.huyanqi.core.dao.BaseDAO;

/**
 * 
 * <p>Title: BaseDAOImpl.java</p>
 * 
 * <p>Description: 抽象泛型dao,entityManager由子类注入</p>
 * 
 * <p>Datetime: 2011-12-25 下午5:39:22</p>
 *
 * @company 成都映潮科技有限公司
 * 
 * @author <a href="mailto:liwei@sunsharp.cn">p.k.ripper</a>
 *
 */
public abstract class BaseDAOImpl<T, PK extends Serializable>  extends AbstractJpaDao<T> implements BaseDAO<T,PK>{
	protected Logger log = Logger.getLogger(this.getClass());

	protected Class<T> entityClass=ReflectionUtils.getSuperClassGenricType(getClass());
	
	private final Set<Class<?>> dataTypes = new HashSet<Class<?>>(Arrays.asList(new Class<?>[] { entityClass }));

	public BaseDAOImpl() {		
		setDefaultMaxResults(-1);
	}
	@Override
	public Set<Class<?>> getTypes() {
		return dataTypes;
	}
	/**
	 * 如有必要，重写此方法，return true;
	 */
	@Override
	public boolean canBeMerged(T o) {
		return false;
	}
	@Override
	public T saveEntity(T e)  throws Exception{
		return store(e);
	}
	@Override
	public T updateEntity(T e)  throws Exception{
		return merge(e);
	}
	@Override
	public void deleteEntity(T e) throws Exception {
		remove(e);
	}
	@Override
	public void deleteEntity(final PK id) throws Exception{
		T entity=findEntityById(id);
		if(null!=entity)deleteEntity(entity);
	}

	@Transactional
	@Override
	public T findEntityById(final PK id) throws Exception{		
		try{
			return getEntityManager().find(entityClass, id);			
		}catch(Exception e){
			log.error(e.getMessage());
			e.printStackTrace();
			return null;
		}
	}	
	/**
	 * 按ID查询多个对象
	 * 
	 * @param ids
	 * @return
	 */
	@Override
	public List<T> findByIds(final Collection<PK> ids) throws Exception{
		List<T> list=new ArrayList<T>();
		if(null!=ids){
			if(ids instanceof java.util.Collection){
				for(Iterator<PK> i=((Collection<PK>) ids).iterator();i.hasNext();){
					T entity=findEntityById(i.next());
					if(null!=entity){
						list.add(entity);
					}
				}
			}
		}
		return list;
	}
	@Override
	public void batchSave(Collection<T> e) throws Exception {		
		if (null!=e) {
			if (e instanceof java.util.Collection) {
				for (Iterator<T> i = ((Collection<T>) e).iterator(); i.hasNext();) {
					try {
							store(i.next());
					} catch (EntityNotFoundException x) {
							// This entity has been deleted - remove it from the collection
						i.remove();
					}
				}
			}
		}
	}
	@Override
	public void batchUpdate(Collection<T> e) throws Exception {
		if (e != null) {
			if (e instanceof java.util.Collection) {
				for (Iterator<T> i = ((Collection<T>) e).iterator(); i.hasNext();) {
					try {
						merge(i.next());
					} catch (EntityNotFoundException x) {
						// This entity has been deleted - remove it from the collection
						i.remove();
					}
				}
			} 
		}
	}
	@Override
	public void batchDelete(Collection<T> e) throws Exception {
		if (e != null) {
			if (e instanceof java.util.Collection) {
				for (Iterator<T> i = ((Collection<T>) e).iterator(); i.hasNext();) {
					try {
						remove(i.next());
					} catch (EntityNotFoundException x) {
						// This entity has been deleted - remove it from the collection
						i.remove();
					}
				}
			} 
		}
	}
	@Override
	public void batchDelete(final PK... ids) throws Exception {
		for (int i = 0; i < ids.length; i++) { 
			try{
				T entity=findEntityById(ids[i]);
				if(null!=entity){
					deleteEntity(entity);
				}
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	
	
	protected Session getSession(){
		Session session=(Session)getEntityManager().getDelegate();
		return session;
	}
	
	@Override
	public Page<T> findPage(final Page<T> page, final String hql, final Object... parameters)
			throws Exception {
		List<T> result = executeQuery(hql, page.getFirst(), page.getPageSize(), parameters);
		if(page.isAutoCount()){
			long totoal=countHqlResult(hql,parameters);
			page.setTotalCount(totoal);
		}
		page.setResult(result);
		return page;
	} 
	
	@SuppressWarnings("unchecked")
	public List<Object> findPageBySQL(final Page<T> page,String sql,Object...parameters)throws Exception{
		Query query  = getSession().createSQLQuery(sql).addEntity(entityClass)
										.setFirstResult(page.getFirst()).setMaxResults(page.getPageSize());
		if(null!=parameters && parameters.length>0){
			for (int i = 0; i < parameters.length; i++) {
				query = query.setParameter(i, parameters[i]);
			}
		}
		List<Object> result = query.list();
		return result;
	}
	
	public Object findSingleResultBySQL(String sql,Object...parameters)throws Exception{
		Query query  = getSession().createSQLQuery(sql);
		if(null!=parameters && parameters.length>0){
			for (int i = 0; i < parameters.length; i++) {
				query = query.setParameter(i, parameters[i]);
			}
		}
		Object object = query.uniqueResult();
		return object;
	}
	@Override
	public Page<T> findPageByNameQuery(final Page<T> page,final String queryName,final Object... parameters) throws Exception{
		List<T> result=this.executeQueryByName(queryName, page.getFirst(), page.getPageSize(), parameters);
		page.setResult(result);
		return page;		
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countHqlResult(final String hql, final Object... parameters) {
		String countHql = prepareCountHql(hql);

		try {
			Long count = findUniqueCountByHql(countHql, parameters);
			return count;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:"
					+ countHql, e);
		}
	}
	/**
	 * 执行count查询获得本次Sql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countSqlResult(final String sql, final Object... parameters) {
		String countHql = prepareCountHql(sql);

		try {
			Long count = findUniqueCountByHql(countHql, parameters);
			return count;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:"
					+ countHql, e);
		}
	}
	private String prepareCountHql(String orgHql) {
		String fromHql = orgHql;
		fromHql = "from " + StringUtils.substringAfter(fromHql, "from");
		fromHql = StringUtils.substringBefore(fromHql, "order by");

		String countHql = "select count(*) " + fromHql;
		return countHql;
	}
	@Override
	@SuppressWarnings("unchecked")
	public <X> X findUniqueCountByHql(String hql, Object... parameters)
			throws Exception {
		return (X) executeQuerySingleResult(hql, parameters);
	}
	/**
	 * 按Criteria分页查询.
	 * 
	 * @param page 分页参数.
	 * @param criterions 数量可变的Criterion.
	 * 
	 * @return 分页查询结果.附带结果列表及所有查询输入参数.
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Page<T> findAllByPage(final Page<T> page,final Criterion... criterions)  throws Exception {
		Criteria c = createCriteria(criterions);

		if (page.isAutoCount()) {
			long totalCount = countCriteriaResult(c);
			page.setTotalCount(totalCount);
		}
		
		setPageParameterToCriteria(c, page);
		List<T> result = c.list();
		page.setResult(result);
		return page;
	}
	
	/**
	 * 按Criteria分页查询.
	 * 
	 * @param page 分页参数.
	 * @param criterions 数量可变的Criterion.
	 * 
	 * @return 分页查询结果.附带结果列表及所有查询输入参数.
	 */
	@Override
	@SuppressWarnings("unchecked")
	public Page<T> findAllByPageDistinct(final Page<T> page,final Criterion... criterions) throws Exception {
		Criteria c = createCriteria(criterions);
		
		c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 
		if (page.isAutoCount()) {
			long totalCount = countCriteriaResult(c);
			page.setTotalCount(totalCount);
		}
		
		setPageParameterToCriteria(c, page);
		List<T> result = c.list();
		page.setResult(result);
		return page;
	}
	
	protected Criteria createCriteria(final Criterion... criterions) {
		Session session=getSession();
		Criteria criteria = session.createCriteria(entityClass);
		for (Criterion c : criterions) {
			if(null!=c)criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 设置分页参数到Criteria对象,辅助函数.
	 */
	protected Criteria setPageParameterToCriteria(final Criteria c,
			final Page<T> page) {

		c.setFirstResult(page.getFirst() );
		c.setMaxResults(page.getPageSize());

		if (page.isOrderBySetted()) {
			String[] orderByArray = StringUtils.split(page.getOrderBy(), ',');
			String[] orderArray = StringUtils.split(page.getOrder(), ',');

			for (int i = 0; i < orderByArray.length; i++) {
				if (Page.ASC.equals(orderArray[i])) {
					c.addOrder(Order.asc(orderByArray[i]));
				} else {
					c.addOrder(Order.desc(orderByArray[i]));
				}
			}
		}
		return c;
	}

	/**
	 * 执行count查询获得本次Criteria查询所能获得的对象总数.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	protected long countCriteriaResult(final Criteria c) {
		CriteriaImpl impl = (CriteriaImpl) c;

		// 先把Projection、ResultTransformer、OrderBy取出来,清空三者后再执行Count操作
		Projection projection = impl.getProjection();
		ResultTransformer transformer = impl.getResultTransformer();

		List<CriteriaImpl.OrderEntry> orderEntries = null;
		try {
			orderEntries = (List<OrderEntry>) ReflectionUtils.getFieldValue(impl,"orderEntries");
			ReflectionUtils.setFieldValue(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			log.error("不可能抛出的异常:" + e.getMessage());
		}

		// 执行Count查询
		Long totalCountObject = (Long) c.setProjection(Projections.rowCount())
				.uniqueResult();
		long totalCount = (totalCountObject != null) ? totalCountObject : 0;

		// 将之前的Projection,ResultTransformer和OrderBy条件重新设回去
		c.setProjection(projection);

		if (projection == null) {
			c.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		if (transformer != null) {
			c.setResultTransformer(transformer);
		}
		try {
			ReflectionUtils.setFieldValue(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			log.error("不可能抛出的异常:" + e.getMessage());
		}

		return totalCount;
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findByProperty(Map<String, Object> values) throws Exception {
		Criterion criterion = Restrictions.allEq(values);  
		Criteria criteria = createCriteria();  
		criteria.add(criterion);  
		return criteria.list(); 
	}

	@Override
	public List<T> findByPropertyLike(String propertyName, Object value)
			throws Exception {
		Criterion criterion = Restrictions.like(propertyName, (String)value,MatchMode.ANYWHERE);
		return findByCriteria(criterion);
	}

	@Override
	public List<T> findByProperty(String propertyName, Object value)
			throws Exception {
		Criterion criterion = Restrictions.eq(propertyName, value);  
		return findByCriteria(criterion);
	}
	@Override
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Criterion... criterions) {  
		return createCriteria(criterions).list();  
	}
	@Override
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(Order[] order,Criterion... criterions){
		Criteria ct=createCriteria(criterions);
		for(Order o:order){
			if(null!=o)ct.addOrder(o);
		}
	   return ct.list();
	}
}
